#include "astraldef.h"
#include "queueapi.h"
#include "sched.h"
#include "mapapi.h"

typedef struct PrioTask
{
    AstralTask at;
    struct QueueNode qn;
} *PrioTask;

static struct Queue prio_q[CFG_CORE_NUM][32];
static struct HashMap task_dict;
#define LOCAL_RUNQ(prio) prio_q[core][prio]

static PrioTask warp_task(AstralTask task)
{
    HashNode hn = find_object(&task_dict, task);
    if (!hn)
    {
        hn = (HashNode)ff_alloc(sizeof(*hn));
        PrioTask pt = (PrioTask)ff_alloc(sizeof(struct PrioTask));
        pt->at = task;
        insert_object(&task_dict, hn, task, pt);
    }
    return CAST_AS(PrioTask, hn->value);
}

static void prio_sched_init()
{
    for(int core = 0; core < CFG_CORE_NUM; core++)
    {
        for (int i = 0; i < 32; i++)
        {
            queue_init(&LOCAL_RUNQ(i));
        }
    }
    init_hashmap(&task_dict);
}


static AstralTask pick_next(int core)
{
    PrioTask pt = NULL;
    QueueNode n = NULL;
    int next_prio = 32;

    for (int i = 0; i < 32; i++)
    {
        n = LOCAL_RUNQ(i).first;
        if (n)
        {
            next_prio = i;
            break;
        }
    }

    if (!n)
    {
        //really, has nothing to run
        return NULL;
    }

    queue_pop(&LOCAL_RUNQ(next_prio));
    pt = CONTAINER_OF(PrioTask, qn, n);

    return pt->at;
}

static AstralTask try_next(int core, AstralTask curr)
{
    PrioTask pt = NULL;
    QueueNode n = NULL;
    int next_prio = curr->priority;

    for (int i = 0; i < next_prio; i++)
    {
        n = queue_pop(&LOCAL_RUNQ(i));
        if (n)
        {
            break;
        }
    }

    /**
     * If high priority preemption is not occurs,
     * let us check is there any same priority round-robin.
     *
     * DY Young, @Xitieying Beijing, 2024 CN
     */
    if(!n && SLICEUP == curr->status)
    {
       n = queue_pop(&LOCAL_RUNQ(next_prio));
    }

    if (!n)
    {
        //really, has nothing to run
        return curr;
    }

    pt = CONTAINER_OF(PrioTask, qn, n);

    return pt->at;
}


static void add_ready(AstralTask task, int core)
{
    int prio = task->priority & 0b11111;
    Queue slotq = &LOCAL_RUNQ(prio);
    PrioTask pt = warp_task(task);
    queue_push(slotq, &pt->qn);
}

static void remove_ready(AstralTask task)
{
    // int prio = task->priority & 0b11111;
    PrioTask pt = warp_task(task);

    Queue slotq = pt->qn.queue;
    boolean status = queue_remove(slotq, &pt->qn);
    while (!status)
    {
    }
}

DEFINE_SCHEDULER(PRIO, add_ready, remove_ready, pick_next, try_next, prio_sched_init);
